
/*
 * Copyright (C) 2008-2009 Archie L. Cobbs. All rights reserved.
 *
 * $Id: AbstractCloseable.java 380 2010-03-11 22:27:58Z archie.cobbs $
 */

package org.dellroad.sidekar.util;

import org.apache.log4j.Logger;

/**
 * Support superclass for {@link Closeable} implementations.
 *
 * <p>
 * Using this superclass will ensure that an error is logged if this instance is finalized before
 * it is closed. Subclasses should clear the {@link #closeNeeded} flag in their {@link #close} implementations
 * so the warning will not emitted when the instance is finalized.
 * </p>
 *
 * <p>
 * Subclasses may also choose to require that a {@link CloseableNotes} instance be associated with the current
 * thread at construction time.
 * </p>
 */
public abstract class AbstractCloseable implements Closeable {

    /**
     * Whether a {@link #close} is still required on this instance.
     */
    protected boolean closeNeeded;

    /**
     * Records the point in the code at which this instance was created.
     */
    private final Exception instantiationPoint = new Exception();

    /**
     * Constructor.
     *
     * <p>
     * This invokes {@link CloseableNotes#add} providing this instance and sets {@link #closeNeeded} to true.
     *
     * <p>
     * If {@code requireNote} is true and there is no {@link CloseableNotes} associated with the current thread,
     * then an {@link IllegalStateException} is thrown.
     *
     * @param requireNote whether to require a {@link CloseableNotes} to be associated with the current thread
     * @throws IllegalStateException if {@code requireNote} is {@code true} but no {@link CloseableNotes} is
     *  associated with the current thread
     */
    protected AbstractCloseable(boolean requireNote) {
        CloseableNotes.addToCurrentAction(this, requireNote);
        this.closeNeeded = true;
    }

    /**
     * Perform the actual close operation as specified by {@link Closeable#close Closeable.close()}.
     * In addition, subclasses must clear the {@link #closeNeeded} flag when they do.
     */
    public abstract void close();

    /**
     * Log an error if {@link #closeNeeded} is still set.
     */
    @Override
    protected void finalize() throws Throwable {
        try {
            if (this.closeNeeded) {
                Logger log = Logger.getLogger(getClass());
                log.error("close() never invoked on " + this + " (stack trace shows instantiation point)", this.instantiationPoint);
            }
        } finally {
            super.finalize();
        }
    }
}

